package org.spring.bean.factory.support;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spring.bean.PropertyValue;
import org.spring.bean.PropertyValues;
import org.spring.bean.exceptions.BeansException;
import org.spring.bean.factory.BeanCreationException;
import org.spring.bean.factory.BeanFactoryAware;
import org.spring.bean.factory.InitializingBean;
import org.spring.bean.factory.config.AutowireCapableBeanFactory;
import org.spring.bean.factory.config.BeanDefinition;
import org.spring.bean.factory.config.BeanPostProcessor;
import org.spring.bean.factory.config.RuntimeBeanNameReference;
import org.tool.core.lang.Assert;
import org.tool.reflect.ConstructorUtil;
import org.tool.reflect.ReflectUtil;
import sun.plugin.com.TypeConverter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Objects;
import java.util.Set;

/**
 * 实现默认 bean 创建的抽象 bean 工厂超类，具有 {@link RootBeanDefinition} 类指定的全部功能。
 * 除了 AbstractBeanFactory 的 {@link #createBean} 方法外，还实现了 {@link AutowireCapableBeanFactory} 接口。
 *
 * <p>提供 bean 创建（使用构造函数解析）、属性填充、连接（包括自动连接）和初始化。
 * 处理运行时 bean 引用、解析托管集合、调用初始化方法等。支持自动装配构造函数、按名称的属性和按类型的属性。
 *
 * <p>子类要实现的主要模板方法是{@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)}，用于按类型自动装配。
 * 如果工厂能够搜索其 bean 定义，匹配的 bean 通常将通过这样的搜索来实现。对于其他工厂风格，可以实现简化的匹配算法。
 *
 * <p>请注意，此类 <i>not<i> 假设或实现 bean 定义注册表功能。
 * 请参阅 {@link DefaultListableBeanFactory} 以了解 {@link org.springframework.beans.factory.ListableBeanFactory}
 * 和 {@link BeanDefinitionRegistry} 接口的实现，它们分别表示此类工厂的 API 和 SPI 视图。
 *
 * @author 李坤
 * @date 2022/4/20 21:49
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private final Logger logger = LoggerFactory.getLogger(AbstractAutowireCapableBeanFactory.class);

    @Getter
    @Setter
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, RootBeanDefinition beanDefinition, Object[] args) {
        Assert.notNull(beanDefinition, "Bean definition must not be null");

        // 根据配置生成 spring bean 对象
        Object bean = this.doCreateBean(beanName, beanDefinition, args);
        // 填充 spring bean 相关属性
        applyPropertyValues(beanName, beanDefinition, beanDefinition.getPropertyValues());
        // 执行 bean 初始化方法
        bean = initializeBean(bean, beanName);

        return bean;
    }

    @Override
    public Object initializeBean(Object existingBean, String beanName) throws BeansException {
        return initializeBean(existingBean, beanName, null);
    }

    /**
     * 初始化给定的bean实例，应用工厂回调以及init方法和bean后处理器。
     * <p>
     * 对于传统定义的bean，从{@link createBean}调用；对于现有的bean实例，从{@link initializeBean}调用。
     *
     * @param beanName           工厂中的 bean 名称(用于调试)
     * @param bean               需要初始化的新 bean 实例
     * @param rootBeanDefinition 创建 bean 时使用的 bean 定义(如果给定现有的 bean 实例，也可以是{@code null})
     *
     * @return 初始化的 bean 实例(可能被包装)
     *
     * @see BeanNameAware
     * @see BeanClassLoaderAware
     * @see BeanFactoryAware
     * @see #applyBeanPostProcessorsBeforeInitialization
     * @see #invokeInitMethods
     * @see #applyBeanPostProcessorsAfterInitialization
     */
    protected Object initializeBean(Object bean, String beanName, RootBeanDefinition rootBeanDefinition) {
        if (Objects.isNull(rootBeanDefinition)) {
            bean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        }

        // 初始化 bean （执行 bean 的初始化方法）
        invokeInitMethods(bean, beanName);

        if (Objects.isNull(rootBeanDefinition)) {
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }

        return bean;
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            // 如果返回结果为空，则不会再继续向下执行
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;

            logger.debug("BeanPostProcessor {} returned a new bean instance of class [{}] with name [{}]", processor, result.getClass(), beanName);
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;

            logger.debug("BeanPostProcessor {} returned a new bean instance of class [{}] with name [{}]", processor, result.getClass(), beanName);
        }
        return result;
    }


    /**
     * 实际创建指定的bean。此时已经进行了预创建处理，例如检查 {@code postProcessBeforeInstantiation} 回调。
     * <p>
     * 区分默认 bean 实例化、使用工厂方法和自动装配构造函数。
     *
     * @param beanName       bean 的名字
     * @param beanDefinition bean 的合并 bean 定义
     * @param args           用于构造函数或工厂方法调用的显式参数
     *
     * @return bean 的新实例
     *
     * @throws BeansException 如果无法创建 bean
     * @see #instantiateBean
     * @see #instantiateUsingFactoryMethod
     * @see #autowireConstructor
     */
    protected Object doCreateBean(String beanName, RootBeanDefinition beanDefinition, Object[] args) throws BeansException {
        Assert.hasText(beanName, "beanName 不能为空");
        Assert.notNull(beanDefinition, "beanDefinition 不能为空");

        logger.debug("开始创建名称为 {} 的 bean", beanName);

        // 根据 bean 的类型创建对象实例
        Object singletonObject;

        // TODO 暂不处理第二种情况
        if (ObjectUtil.isEmpty(args)) {
            singletonObject = instantiationStrategy.instantiate(beanDefinition, beanName, null);
        } else {
            // 获取匹配的 bean 的构造函数
            Constructor<?> constructor = ConstructorUtil.getMatchedConstructor(beanDefinition.getBeanClass(), args);
            singletonObject = instantiationStrategy.instantiate(beanDefinition, beanName, null, constructor, args);
        }
        // 注册对象实例（在设置属性前注册 bean ，以解决部分循环依赖问题）
        this.registerSingleton(beanName, singletonObject);

        return singletonObject;
    }

    /**
     * 应用给定的属性值，解析对此 bean 工厂中其他 bean 的任何运行时引用。
     * 必须使用深拷贝，所以我们不会永久修改这个属性。
     *
     * @param beanName       传递的 bean 名称以获得更好的异常信息
     * @param beanDefinition 合并的 bean 定义
     * @param propertyValues 新的属性值
     */
    protected void applyPropertyValues(String beanName, BeanDefinition beanDefinition, PropertyValues propertyValues) {
        logger.debug("开始为 {} 对象填充属性 {}", beanName, propertyValues);

        final Object bean = getBean(beanName, beanDefinition.getBeanClass());

        if (Objects.nonNull(propertyValues) && Objects.nonNull(bean)) {
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                Object value = propertyValue.getValue();

                // 如果是 bean 的引用，则获取引用的 bean 对象
                if (value instanceof RuntimeBeanNameReference) {
                    final String referenceBeanName = ((RuntimeBeanNameReference) value).getBeanName();

                    logger.debug("解析 bean 名称引用 {}", referenceBeanName);

                    value = getBean(referenceBeanName);
                }
                InnerFieldUtil.setAttribute(bean, propertyValue.getName(), value);
            }
        }
    }

    /**
     * 如果 bean 实现了 {@link InitializingBean} 接口，则调用其 {@link InitializingBean#afterPropertiesSet()} 方法。
     *
     * @param existingBean 已经创建的 bean
     * @param beanName     bean 的名字
     */
    protected void invokeInitMethods(Object existingBean, String beanName) {
        if (existingBean instanceof InitializingBean) {
            try {
                ((InitializingBean) existingBean).afterPropertiesSet();
            } catch (Exception exception) {
                throw new BeanCreationException("调用 InitializingBean#afterPropertiesSet() 方法失败");
            }
            logger.debug("调用 {} 对象的 afterPropertiesSet() 方法", beanName);
        }
    }

    /*-----------------------------------------------public static class----------------------------------------------*/

    public static class InnerFieldUtil {

        /**
         * 为属性赋值
         *
         * @param bean  bean 对象
         * @param name  属性名称
         * @param value 属性值
         */
        public static void setAttribute(Object bean, String name, Object value) {
            final Class<?> clazz = bean.getClass();

            try {
                Field field = clazz.getDeclaredField(name);
                ReflectUtil.setFieldValue(bean, field, convertPropertity(field, value));
            } catch (NoSuchFieldException exception) {
                throw new IllegalArgumentException(clazz + " 不存在属性 " + name);
            }
        }

        /**
         * 将字符串类型的属性值转换为字段对应的类型
         *
         * @param field         字段
         * @param propertyValue 属性值
         *
         * @return 转换后的属性值
         */
        private static Object convertPropertity(Field field, Object propertyValue) {
            final Class<?> type = field.getType();
            if (propertyValue instanceof String && !String.class.equals(type)) {
                propertyValue = Convert.convert(type, propertyValue);
            }
            return propertyValue;
        }

    }

}
